package com.loong.tool.secret.security.encrypt;

import java.security.GeneralSecurityException;
import java.security.KeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.SecretKeySpec;

import com.loong.tool.secret.base.Base64;
import com.loong.tool.secret.base.UTF8;
import com.loong.tool.secret.exception.InvalidCodeException;
import com.loong.tool.secret.exception.InvalidCryptoException;
import com.loong.tool.secret.exception.InvalidKeyException;
import com.loong.tool.secret.exception.SecretVariableException;

/**
 * 对称加密处理类
 * 
 * @author 张成轩
 */
public class Symmetric {

	/**
	 * 加密算法
	 */
	public enum Algorithm {

		/** DES算法 */
		DES("DES"),
		/** AES算法 */
		AES("AES");

		private String value;

		private Algorithm(String value) {

			this.value = value;
		}
	}

	/** 默认加密类型 */
	private static final Algorithm DEFAULT = Algorithm.AES;

	/**
	 * 生成随机密钥
	 * 
	 * @return 密钥
	 */
	public static String getKey() {

		// 获取密钥对象
		return getKey(DEFAULT);
	}

	/**
	 * 生成随机密钥
	 * 
	 * @param algorithm 算法
	 * @return 密钥
	 */
	public static String getKey(Algorithm algorithm) {

		// 获取密钥对象
		KeyGenerator kg;
		try {
			kg = KeyGenerator.getInstance(algorithm.value);
		} catch (NoSuchAlgorithmException e) {
			throw new SecretVariableException("Symmetric variable init error: " + algorithm.name());
		}
		return Base64.encode(kg.generateKey().getEncoded());
	}

	/**
	 * 通过密钥种子生成密钥
	 * 
	 * @param seed 种子
	 * @return 密钥
	 */
	public static String getKeyBySeed(String seed) {

		return getKeyBySeed(seed, DEFAULT);
	}

	/**
	 * 通过密钥种子生成密钥
	 * 
	 * @param seed 种子
	 * @param algorithm 算法
	 * @return 密钥
	 */
	public static String getKeyBySeed(String seed, Algorithm algorithm) {

		// 密钥生成器
		SecureRandom random = new SecureRandom(UTF8.getBytes(seed));
		// 密钥对象
		KeyGenerator kg;
		try {
			kg = KeyGenerator.getInstance(algorithm.value);
		} catch (NoSuchAlgorithmException e) {
			throw new SecretVariableException("Symmetric variable init error: " + algorithm.name());
		}
		// 初始化密钥对象
		kg.init(random);
		return Base64.encode(kg.generateKey().getEncoded());
	}

	/**
	 * 通过密钥生成密钥对象
	 * 
	 * @param key 密钥
	 * @param algorithm 算法
	 * @return 密钥对象
	 * @throws InvalidKeyException 无效密钥异常
	 */
	private static SecretKeySpec getKeySpec(String key, Algorithm algorithm) throws InvalidKeyException {

		// 获取密钥对象
		try {
			return new SecretKeySpec(Base64.decode(key), algorithm.value);
		} catch (InvalidCodeException e) {
			throw new InvalidKeyException(e);
		}
	}

	/**
	 * 加密
	 * 
	 * @param str 明文
	 * @param key 密钥
	 * @return 密文
	 * @throws InvalidKeyException 无效密钥异常
	 */
	public static String encrypt(String str, String key) throws InvalidKeyException {

		return encrypt(str, key, DEFAULT);
	}

	/**
	 * 加密
	 * 
	 * @param str 明文
	 * @param key 密钥
	 * @param algorithm 算法
	 * @return 密文
	 * @throws InvalidKeyException 无效密钥异常
	 */
	public static String encrypt(String str, String key, Algorithm algorithm) throws InvalidKeyException {

		try {
			// 密钥处理对象
			Cipher cipher = Cipher.getInstance(algorithm.value);
			// 设置加密模式
			cipher.init(Cipher.ENCRYPT_MODE, getKeySpec(key, algorithm));
			return Base64.encode(cipher.doFinal(UTF8.getBytes(str)));

		} catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
			throw new SecretVariableException("Symmetric variable init error: " + algorithm.name());
		} catch (GeneralSecurityException e) {
			throw new InvalidKeyException(e);
		}
	}

	/**
	 * 解密
	 * 
	 * @param crypto 密文
	 * @param key 密钥
	 * @return 明文
	 * @throws InvalidKeyException 无效密钥异常
	 * @throws InvalidCryptoException 无效密文异常
	 */
	public static String decrypt(String crypto, String key) throws InvalidCryptoException, InvalidKeyException {

		return decrypt(crypto, key, DEFAULT);
	}

	/**
	 * 解密
	 * 
	 * @param crypto 密文
	 * @param key 密钥
	 * @param algorithm 算法
	 * @return 明文
	 * @throws InvalidKeyException 无效密钥异常
	 * @throws InvalidCryptoException 无效密文异常
	 */
	public static String decrypt(String crypto, String key, Algorithm algorithm)
			throws InvalidKeyException, InvalidCryptoException {

		try {

			// 密钥处理对象
			Cipher cipher = Cipher.getInstance(algorithm.value);
			// 设置解密模式
			cipher.init(Cipher.DECRYPT_MODE, getKeySpec(key, algorithm));
			return UTF8.newString(cipher.doFinal(Base64.decode(crypto)));

		} catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
			throw new SecretVariableException("Symmetric variable init error: " + algorithm.name());
		} catch (KeyException e) {
			throw new InvalidKeyException(e);
		} catch (GeneralSecurityException | InvalidCodeException e) {
			throw new InvalidCryptoException(e);
		}
	}
}
